#include <string>
#include <vector>
#include <iostream>
#include <fstream>
#include <time.h>
#include "Dinosaur.h"
#include "IOMgmt.h"
#include "Point.h"
#include "Battle.h"
#include "Egg.h"
using namespace std;
int PlantSpawnRate;
int PlantSpawnAmount;
int simTimer;
int dinoCount;
char map[100][100];
struct species
{
	string name;
	int number;
};
void construct();
void printmap();
void plantSpawn();
void simulate(ostream&);
void getBehaviors();
void executeBehaviors();
Point getClosestDino(Point);
int getClosestPlant(Point);
bool moveToDestination(int, Point);
void determineBattle(int);
void ageEggs();
void battleControl();
vector<Dinosaur> DinoList;
vector<species> SpeciesList;
vector<Point> PlantList;
vector<Battle> BattleList;
vector<Egg> EggList;
IOMgmt::InMgr finMgr("Enter name of Input file:");
ifstream&  fin = finMgr.getStream();
IOMgmt::OutMgr foutMgr("Enter name of Output file:");
ostream&   fout = foutMgr.getStream();
int main()
{

	srand(time(NULL));
	construct();
	//printmap();
	simulate(fout);
	system("pause");
}
void construct()
{
	dinoCount=0;
	simTimer=0;
	for(int x=0; x<100; x++)
		for(int y=0; y<100; y++)
			map[x][y]=' ';
	int temp;
	string token;
	fin>>PlantSpawnRate;
	cout<<"plant spawn rate is "<<PlantSpawnRate<<endl;
	fin>>PlantSpawnAmount;
	cout<<"plant spawn amount is "<<PlantSpawnAmount<<endl;
	
	temp = fin.tellg();
	fin >> token;
	fin.seekg(temp);
	//populates initial list of dinosaurs and adds their locations to the map
	while(token!="END")
	{
		Dinosaur a;
		bool foundSpecies=false;
		a.Extract(fin);
		a.setID(DinoList.size()+1);
		DinoList.push_back(a);
		dinoCount++;
		for(int x=0; x<SpeciesList.size(); x++)
		{
			if(a.getSpecies()==SpeciesList[x].name)
			{
				foundSpecies=true;
				SpeciesList[x].number++;
			}
		}
		if(!foundSpecies)
		{
			species s;
			s.name=a.getSpecies();
			s.number=1;
			SpeciesList.push_back(s);
		}
		temp = fin.tellg();
		fin >> token;
		fin.seekg(temp);
		map[a.getLocation().getX()][a.getLocation().getY()]=a.getCarn()?'C':'H';
	}
	for(int x=0; x<DinoList.size(); x++)
	{
		DinoList[x].getInfo();
		cout<<endl;
	}
	for(int x=0; x<SpeciesList.size(); x++)
	{
		cout<<"there are/is "<<SpeciesList[x].number<<" "<<SpeciesList[x].name<<"(s)"<<endl;
	}
	plantSpawn();
}
void printmap()
{
	for(int x=0; x<201; x++)
		cout<<"_";
	cout<<endl;
	for(int x=0; x<100; x++)
	{
		cout<<"|";
		for(int y=0; y<100; y++)
		{
			if(map[x][y]!=' ')
				cout<<map[x][y];
			else
				cout<<"_";
			cout<<"|";
		}
		cout<<endl;
	}
}
void plantSpawn()
{
	int xpos, ypos, x=0;
	while(x<PlantSpawnAmount)
	{
		xpos=rand()%100;
		ypos=rand()%100;
		if(map[xpos][ypos]==' ')
		{
			x++;
			map[xpos][ypos]='P';
			PlantList.push_back(Point(xpos, ypos));
		}
	}
}
void simulate(ostream& fout)
{
	while(DinoList.size()>0)
	{
		getBehaviors();
		executeBehaviors();
		simTimer++;

		if(simTimer > 720)
		{
			cout<<"\n\n\n\nAfter one month the population is:\n\n\n"<<endl;
			fout<<"\n\n\n\nAfter one month the population is:\n\n\n"<<endl;

			for(int x=0; x<SpeciesList.size(); x++)
			{
				cout<<"there are/is "<<SpeciesList[x].number<<" "<<SpeciesList[x].name<<"(s)"<<endl;
				fout<<"there are/is "<<SpeciesList[x].number<<" "<<SpeciesList[x].name<<"(s)"<<endl;
			}
			
			break;
		}

		if(simTimer%PlantSpawnRate==0)
			plantSpawn();
		ageEggs();
		battleControl();

		//if (simTimer % 24)
			//printmap();
		//system("pause");
	}
}
void getBehaviors()
{
	for(int x=0; x<DinoList.size(); x++)
	{
		DinoList[x].determineBehavior();
	}
}
void executeBehaviors()
{
	fout<<"Current Turn: "<<simTimer<<endl;
	string behavior;
	for(int x=0; x<DinoList.size(); x++)
	{
		behavior=DinoList[x].getBehavior();
		fout<<"\tDinosaur "<<DinoList[x].getID()<<" \t"<<DinoList[x].getSpecies();
		if (DinoList[x].getSpecies() == "Trex" || DinoList[x].getSpecies() == "Raptor" || DinoList[x].getSpecies() == "Compy")
			fout<<"\t";

		fout<<"\tindex: "<<x<<" \tstate: "<<DinoList[x].getBehavior()<<endl;

		if(behavior=="death")
		{
			for(int z=0; z<SpeciesList.size(); z++)
			{
				if(SpeciesList[z].name==DinoList[x].getSpecies())
					SpeciesList[z].number--;
				if(SpeciesList[z].number==0)
				{
					fout<<SpeciesList[z].name<<" has gone extinct"<<endl;
					cout<<SpeciesList[z].name<<" has gone extinct"<<endl;
					SpeciesList.erase(SpeciesList.begin()+z);
					break;
				}
			}
			map[DinoList[x].getLocation().getX()][DinoList[x].getLocation().getY()]=' ';
			DinoList.erase(DinoList.begin()+x);
			x--;
			continue;
		}
		if(behavior=="battle")
			continue;
		if(behavior=="wander")
		{
			moveToDestination(x, Point(rand()%100, rand()%100));
		}
		if(behavior=="eat"||behavior=="starving")
		{
			if(DinoList[x].getCarn())
			{
				if(moveToDestination(x, getClosestDino(DinoList[x].getLocation())))
					determineBattle(x);
			}
			else
			{
				if(PlantList.size()>0)
				{
					if(moveToDestination(x, PlantList[getClosestPlant(DinoList[x].getLocation())]))
					{
						DinoList[x].eat();
						map[PlantList[getClosestPlant(DinoList[x].getLocation())].getX()][PlantList[getClosestPlant(DinoList[x].getLocation())].getY()]=' ';
						PlantList.erase(PlantList.begin()+getClosestPlant(DinoList[x].getLocation()));
					}
				}

			}
		}
		if(behavior=="lay")
		{
			if(map[DinoList[x].getLocation().getX()+1][DinoList[x].getLocation().getY()]==' ')
			{
				map[DinoList[x].getLocation().getX()+1][DinoList[x].getLocation().getY()]='E';
				EggList.push_back(Egg(DinoList[x].getSpecies(), DinoList[x].getCarn(), DinoList[x].getAttack(), DinoList[x].getSpeed(), DinoList[x].getDefense(), DinoList[x].getMaxHealth(), DinoList[x].getHungerDepletion(), DinoList[x].getReproductionRate(), DinoList[x].getLocation().getX()+1,DinoList[x].getLocation().getY(), DinoList[x].getID()));
				DinoList[x].setHasEgg(true);
				DinoList[x].setBehavior("guard");
			}

		}
	}
}
Point getClosestDino(Point initLoc)
{
	int closest[2];
	closest[0]=1000;
	closest[1]=0;
	int xdif, ydif;
	for(int x=0; x<DinoList.size(); x++)
	{
		if(DinoList[x].getInBattle())
			continue;
		xdif=DinoList[x].getLocation().getX()-initLoc.getX();
		if(xdif<0)
			xdif*=-1;
		ydif=DinoList[x].getLocation().getY()-initLoc.getY();
		if(ydif<0)
			ydif*=-1;
		if(ydif+xdif!=0&&ydif+xdif<closest[0])
		{
			closest[0]=ydif+xdif;
			closest[1]=x;
		}
	}
	return DinoList[closest[1]].getLocation();
}
int getClosestPlant(Point initLoc)
{
	int closest[2];
	closest[0]=1000;
	closest[1]=0;
	int xdif, ydif;
	for(int x=0; x<PlantList.size(); x++)
	{
		xdif=PlantList[x].getX()-initLoc.getX();
		if(xdif<0)
			xdif*=-1;
		ydif=PlantList[x].getY()-initLoc.getY();
		if(ydif<0)
			ydif*=-1;
		if(ydif+xdif!=0&&ydif+xdif<closest[0])
		{
			closest[0]=ydif+xdif;
			closest[1]=x;
		}
	}
	return closest[1];
}
bool moveToDestination(int dino, Point dest)
{
	int countmove=0;
	int xdif, ydif;
	while(countmove<DinoList[dino].getSpeed())
	{
		char oldchar=map[DinoList[dino].getLocation().getX()][DinoList[dino].getLocation().getY()];
		if(DinoList[dino].getLocation().getX()<dest.getX()&&map[DinoList[dino].getLocation().getX()+1][DinoList[dino].getLocation().getY()]==' ')
		{
			map[DinoList[dino].getLocation().getX()][DinoList[dino].getLocation().getY()]=' ';
			DinoList[dino].setLocation(Point(DinoList[dino].getLocation().getX()+1, DinoList[dino].getLocation().getY()));
			map[DinoList[dino].getLocation().getX()][DinoList[dino].getLocation().getY()]=oldchar;
			countmove++;
			continue;
		}
		if(DinoList[dino].getLocation().getX()>dest.getX()&&map[DinoList[dino].getLocation().getX()-1][DinoList[dino].getLocation().getY()]==' ')
		{
			map[DinoList[dino].getLocation().getX()][DinoList[dino].getLocation().getY()]=' ';
			DinoList[dino].setLocation(Point(DinoList[dino].getLocation().getX()-1, DinoList[dino].getLocation().getY()));
			map[DinoList[dino].getLocation().getX()][DinoList[dino].getLocation().getY()]=oldchar;
			countmove++;
			continue;
		}
		if(DinoList[dino].getLocation().getY()<dest.getY()&&map[DinoList[dino].getLocation().getX()][DinoList[dino].getLocation().getY()+1]==' ')
		{
			map[DinoList[dino].getLocation().getX()][DinoList[dino].getLocation().getY()]=' ';
			DinoList[dino].setLocation(Point(DinoList[dino].getLocation().getX(), DinoList[dino].getLocation().getY()+1));
			map[DinoList[dino].getLocation().getX()][DinoList[dino].getLocation().getY()]=oldchar;
			countmove++;
			continue;
		}
		if(DinoList[dino].getLocation().getY()>dest.getY()&&map[DinoList[dino].getLocation().getX()][DinoList[dino].getLocation().getY()-1]==' ')
		{
			map[DinoList[dino].getLocation().getX()][DinoList[dino].getLocation().getY()]=' ';
			DinoList[dino].setLocation(Point(DinoList[dino].getLocation().getX(), DinoList[dino].getLocation().getY()-1));
			map[DinoList[dino].getLocation().getX()][DinoList[dino].getLocation().getY()]=oldchar;
			countmove++;
			continue;
		}
		countmove++;
		xdif=DinoList[dino].getLocation().getX()-dest.getX();
		if(xdif<0)
			xdif*=-1;
		ydif=DinoList[dino].getLocation().getY()-dest.getY();
		if(ydif<0)
			ydif*=-1;
		if(ydif+xdif==1)
			return true;
	}
	return false;
}
void determineBattle(int dino)
{
	Point closest;
	bool foundhelp=false;
	closest=getClosestDino(DinoList[dino].getLocation());
	for(int x=0; x<DinoList.size(); x++)
	{
		if(closest.getX()==DinoList[x].getLocation().getX()&&closest.getY()==DinoList[x].getLocation().getY())
		{
			DinoList[dino].setInBattle(true);
			DinoList[x].setBehavior("battle");
			DinoList[x].setInBattle(true);
			DinoList[x].setBehavior("battle");
			if(!DinoList[x].getCarn())
			{
				string dinotype=DinoList[x].getSpecies();
				int distance=DinoList[x].getSpeed()*2;
				for(int y=0; y<DinoList.size(); y++)
				{
					if(dinotype==DinoList[y].getSpecies()&&x!=y)
					{
						int xdif, ydif;
						xdif=DinoList[x].getLocation().getX()-DinoList[y].getLocation().getX();
						if(xdif<0)
							xdif*=-1;
						ydif=DinoList[x].getLocation().getY()-DinoList[y].getLocation().getY();
						if(ydif<0)
							ydif*=-1;
						if(xdif+ydif<distance)
						{
							DinoList[y].setInBattle(true);
							DinoList[y].setBehavior("battle");
							Battle b(Battle(DinoList[dino], DinoList[x], DinoList[y]));
							b.setStart(simTimer);
							BattleList.push_back(b);
							return;
						}

					}
				}
				Battle b(Battle(DinoList[dino], DinoList[x]));
				b.setStart(simTimer);
				BattleList.push_back(b);
				return;
			}
			else
			{
				Battle b(Battle(DinoList[dino], DinoList[x]));
				b.setStart(simTimer);
				BattleList.push_back(b);
				return;
			}
		}
	}
}


void ageEggs()
{
	for(int x=0; x<EggList.size(); x++)
	{
		if(EggList.size()==0)
			break;
		if(EggList[x].ageEgg())
		{
			map[EggList[x].getLocation().getX()][EggList[x].getLocation().getY()]=EggList[x].getCarn()?'C':'H';
			for(int y=0; y<DinoList.size(); y++)
			{
				if(EggList[x].getParentID()==DinoList[y].getID())
					DinoList[y].setHasEgg(false);
			}
			Dinosaur d;
			d.hatch(EggList[x].getSpecies(), EggList[x].getCarn(), EggList[x].getAttack(), EggList[x].getSpeed(), EggList[x].getDefense(), EggList[x].getMaxHealth(), EggList[x].getHungerDepletion(), EggList[x].getReproductionRate(), EggList[x].getLocation().getX(), EggList[x].getLocation().getY(), dinoCount+1);
			DinoList.push_back(d);
			dinoCount++;
			for(int z=0; z<SpeciesList.size(); z++)
			{
				if(SpeciesList[z].name==d.getSpecies())
					SpeciesList[z].number++;
			}
			fout<<"\tmade new dinosaur"<<endl;
			EggList.erase(EggList.begin()+x);
			x--;
		}
	}
}
void battleControl()
{
	for(int x=0; x<BattleList.size(); x++)
	{
		if(simTimer-BattleList[x].getStart()>=5)
		{
			vector<Dinosaur> d;
			d=BattleList[x].SimBattle();
			for(int y=0; y<DinoList.size(); y++)
			{
				if(d[0].getID()==DinoList[y].getID())
				{
					DinoList[y]=d[0];
					DinoList[y].setInBattle(false);
				}
				if(d[1].getID()==DinoList[y].getID())
				{
					DinoList[y]=d[1];
					DinoList[y].setInBattle(false);
					int xpos, ypos;
					while (DinoList[y].getFlee())
					{
						xpos=rand()%100;
						ypos=rand()%100;
						if(map[xpos][ypos]==' ')
						{
							DinoList[y].setLocation(Point(xpos,ypos));
							DinoList[y].setFlee(false);
							fout<<"\tDinosaur "<<DinoList[y].getSpecies()<<" "<<DinoList[y].getID()<<" has fled from battle"<<endl;
						}
					}
				}
				if(d.size()==3)
					if(d[2].getID()==DinoList[y].getID())
					{
						DinoList[y]=d[2];
						DinoList[y].setInBattle(false);
					}
				
			}
			BattleList.erase(BattleList.begin()+x);
			//x--;
		}
	}
}